/* Microsoft Reference Implementation for TPM 2.0
 *
 *  The copyright in this software is being made available under the BSD License,
 *  included below. This software may be subject to other third party and
 *  contributor rights, including patent rights, and no such rights are granted
 *  under this license.
 *
 *  Copyright (c) Microsoft Corporation
 *
 *  All rights reserved.
 *
 *  BSD License
 *
 *  Redistribution and use in source and binary forms, with or without modification,
 *  are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this list
 *  of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice, this
 *  list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS""
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 *  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
//** Introduction
/*
    This clause contains the functions used for ticket computations.
*/

//** Includes
#include "Tpm.h"
#include "Marshal.h"

//** Functions

//*** TicketIsSafe()
// This function indicates if producing a ticket is safe.
// It checks if the leading bytes of an input buffer is TPM_GENERATED_VALUE
// or its substring of canonical form.  If so, it is not safe to produce ticket
// for an input buffer claiming to be TPM generated buffer
//  Return Type: BOOL
//      TRUE(1)         safe to produce ticket
//      FALSE(0)        not safe to produce ticket
BOOL
TicketIsSafe(
    TPM2B           *buffer
    )
{
    TPM_CONSTANTS32 valueToCompare = TPM_GENERATED_VALUE;
    BYTE            bufferToCompare[sizeof(valueToCompare)];
    BYTE            *marshalBuffer;
//
    // If the buffer size is less than the size of TPM_GENERATED_VALUE, assume
    // it is not safe to generate a ticket
    if(buffer->size < sizeof(valueToCompare))
        return FALSE;
    marshalBuffer = bufferToCompare;
    TPM_CONSTANTS32_Marshal(&valueToCompare, &marshalBuffer, NULL);
    if(MemoryEqual(buffer->buffer, bufferToCompare, sizeof(valueToCompare)))
        return FALSE;
    else
        return TRUE;
}

//*** TicketComputeVerified()
// This function creates a TPMT_TK_VERIFIED ticket.
/*(See part 2 specification)
//  The ticket is computed as:
//      HMAC(proof, (TPM_ST_VERIFIED | digest | keyName))
//  Where:
//      HMAC()              an HMAC using the hash of proof
//      proof               a TPM secret value associated with the hierarchy
//                          associated with keyName
//      TPM_ST_VERIFIED     a value to differentiate the tickets
//      digest              the signed digest
//      keyName             the Name of the key that signed digest
*/
/*
This command causes the TPM to sign an externally provided hash with the specified symmetric or asymmetric signing key.
NOTE 1 If keyhandle references an unrestricted signing key, a digest can be signed using either this command or an HMAC command.
If keyHandle references a restricted signing key, then validation shall be provided, indicating that the TPM performed 
the hash of the data and validation shall indicate that hashed data did not start with TPM_GENERATED_VALUE.
NOTE 2 If the hashed data did start with TPM_GENERATED_VALUE, then the validation will be a NULL ticket.
The x509sign attribute of keyHandle may not be SET (TPM_RC_ATTRIBUTES).
If the scheme of keyHandle is not TPM_ALG_NULL, then inScheme shall either be the same scheme as keyHandle or TPM_ALG_NULL. 
If the sign attribute is not SET in the key referenced by handle then the TPM shall return TPM_RC_KEY.
If the scheme of keyHandle is TPM_ALG_NULL, the TPM will sign using inScheme; otherwise, it will sign using the scheme of keyHandle.
NOTE 3 When the signing scheme uses a hash algorithm, the algorithm is defined in the qualifying data of the scheme. This 
is the same algorithm that is required to be used in producing digest. 
The size of digest must match that of the hash algorithm in the scheme.
此命令使 TPM 使用指定的对称或非对称签名密钥对外部提供的散列进行签名。
注 1 如果密钥句柄引用不受限制的签名密钥，则可以使用此命令或 HMAC 命令对摘要进行签名。
如果 keyHandle 引用受限签名密钥，则应提供验证，表明 TPM 执行了数据散列，并且验证应表明散列数据不是以 TPM_GENERATED_VALUE 开头。
注 2 如果散列数据确实以 TPM_GENERATED_VALUE 开头，则验证将是 NULL 票证。
keyHandle 的 x509sign 属性可能未设置 (TPM_RC_ATTRIBUTES)。
如果 keyHandle 的方案不是 TPM_ALG_NULL，则 inScheme 应与 keyHandle 或 TPM_ALG_NULL 的方案相同。
如果句柄引用的密钥中未设置符号属性，则 TPM 应返回 TPM_RC_KEY。
如果keyHandle的scheme是TPM_ALG_NULL，TPM会使用inScheme进行签名； 否则，它将使用 keyHandle 方案进行签名。
注3：当签名方案使用散列算法时，该算法在方案的合格数据中定义。 这与生成摘要所需的算法相同。
摘要的大小必须与方案中的散列算法的大小相匹配。

*/
void
TicketComputeVerified(
    TPMI_RH_HIERARCHY    hierarchy,     // IN: hierarchy constant for ticket
    TPM2B_DIGEST        *digest,        // IN: digest
    TPM2B_NAME          *keyName,       // IN: name of key that signed the values
    TPMT_TK_VERIFIED    *ticket         // OUT: verified ticket
    )
{
    TPM2B_PROOF         *proof;
    HMAC_STATE           hmacState;
//
    // Fill in ticket fields
    ticket->tag = TPM_ST_VERIFIED;
    ticket->hierarchy = hierarchy;
    proof = HierarchyGetProof(hierarchy);

    // Start HMAC using the proof value of the hierarchy as the HMAC key
    ticket->digest.t.size = CryptHmacStart2B(&hmacState, CONTEXT_INTEGRITY_HASH_ALG,
                                             &proof->b);
        //  TPM_ST_VERIFIED
    CryptDigestUpdateInt(&hmacState, sizeof(TPM_ST), ticket->tag);
        //  digest
    CryptDigestUpdate2B(&hmacState.hashState, &digest->b);
        // key name
    CryptDigestUpdate2B(&hmacState.hashState, &keyName->b);
        // done
    CryptHmacEnd2B(&hmacState, &ticket->digest.b);

    return;
}

//*** TicketComputeAuth()
// This function creates a TPMT_TK_AUTH ticket.
/*(See part 2 specification)
//  The ticket is computed as:
//      HMAC(proof, (type || timeout || timeEpoch || cpHash
//                        || policyRef || keyName))
//  where:
//      HMAC()      an HMAC using the hash of proof
//      proof       a TPM secret value associated with the hierarchy of the key
//                  associated with keyName.
//      type        a value to differentiate the tickets.  It could be either
//                  TPM_ST_AUTH_SECRET or TPM_ST_AUTH_SIGNED
//      timeout     TPM-specific value indicating when the authorization expires
//      timeEpoch   TPM-specific value indicating the epoch for the timeout
//      cpHash      optional hash (digest only) of the authorized command
//      policyRef   optional reference to a policy value
//      keyName name of the key that signed the authorization
*/
void
TicketComputeAuth(
    TPM_ST               type,          // IN: the type of ticket.
    TPMI_RH_HIERARCHY    hierarchy,     // IN: hierarchy constant for ticket
    UINT64               timeout,       // IN: timeout
    BOOL                 expiresOnReset,// IN: flag to indicate if ticket expires on
                                        //      TPM Reset
    TPM2B_DIGEST        *cpHashA,       // IN: input cpHashA
    TPM2B_NONCE         *policyRef,     // IN: input policyRef
    TPM2B_NAME          *entityName,    // IN: name of entity
    TPMT_TK_AUTH        *ticket         // OUT: Created ticket
    )
{
    TPM2B_PROOF         *proof;
    HMAC_STATE           hmacState;
//
    // Get proper proof
    proof = HierarchyGetProof(hierarchy);

    // Fill in ticket fields
    ticket->tag = type;
    ticket->hierarchy = hierarchy;

    // Start HMAC with hierarchy proof as the HMAC key
    ticket->digest.t.size = CryptHmacStart2B(&hmacState, CONTEXT_INTEGRITY_HASH_ALG,
                                             &proof->b);
        //  TPM_ST_AUTH_SECRET or TPM_ST_AUTH_SIGNED,
    CryptDigestUpdateInt(&hmacState, sizeof(UINT16), ticket->tag);
    // cpHash
    CryptDigestUpdate2B(&hmacState.hashState, &cpHashA->b);
        //  policyRef
    CryptDigestUpdate2B(&hmacState.hashState, &policyRef->b);
        //  keyName
    CryptDigestUpdate2B(&hmacState.hashState, &entityName->b);
        //  timeout
    CryptDigestUpdateInt(&hmacState, sizeof(timeout), timeout);
    if(timeout != 0)
    {
            //  epoch
        CryptDigestUpdateInt(&hmacState.hashState, sizeof(CLOCK_NONCE),
                             g_timeEpoch);
            // reset count
        if(expiresOnReset)
            CryptDigestUpdateInt(&hmacState.hashState, sizeof(gp.totalResetCount),
                                 gp.totalResetCount);
    }
        // done
    CryptHmacEnd2B(&hmacState, &ticket->digest.b);

    return;
}

//*** TicketComputeHashCheck()
// This function creates a TPMT_TK_HASHCHECK ticket.
/*(See part 2 specification)
//  The ticket is computed as:
//      HMAC(proof, (TPM_ST_HASHCHECK || digest ))
//  where:
//      HMAC()  an HMAC using the hash of proof
//      proof   a TPM secret value associated with the hierarchy
//      TPM_ST_HASHCHECK
//              a value to differentiate the tickets
//      digest  the digest of the data
*/
void
TicketComputeHashCheck(
    TPMI_RH_HIERARCHY    hierarchy,     // IN: hierarchy constant for ticket
    TPM_ALG_ID           hashAlg,       // IN: the hash algorithm for 'digest'
    TPM2B_DIGEST        *digest,        // IN: input digest
    TPMT_TK_HASHCHECK   *ticket         // OUT: Created ticket
    )
{
    TPM2B_PROOF         *proof;
    HMAC_STATE           hmacState;
//
    // Get proper proof
    proof = HierarchyGetProof(hierarchy);

    // Fill in ticket fields
    ticket->tag = TPM_ST_HASHCHECK;
    ticket->hierarchy = hierarchy;

    // Start HMAC using hierarchy proof as HMAC key
    ticket->digest.t.size = CryptHmacStart2B(&hmacState, CONTEXT_INTEGRITY_HASH_ALG,
                                             &proof->b);
        //  TPM_ST_HASHCHECK
    CryptDigestUpdateInt(&hmacState, sizeof(TPM_ST), ticket->tag);
        //  hash algorithm
    CryptDigestUpdateInt(&hmacState, sizeof(hashAlg), hashAlg);
        //  digest
    CryptDigestUpdate2B(&hmacState.hashState, &digest->b);
        // done
    CryptHmacEnd2B(&hmacState, &ticket->digest.b);

    return;
}

//*** TicketComputeCreation()
// This function creates a TPMT_TK_CREATION ticket.
/*(See part 2 specification)
// The ticket is computed as:
//      HMAC(proof, (TPM_ST_CREATION || Name || hash(TPMS_CREATION_DATA)))
//  Where:
//  HMAC()  an HMAC using the hash of proof
//  proof   a TPM secret value associated with the hierarchy associated with Name
//  TPM_ST_VERIFIED     a value to differentiate the tickets
//  Name    the Name of the object to which the creation data is to be associated
//  TPMS_CREATION_DATA  the creation data structure associated with Name
*/
/*
HMAC(proof, (TPM_ST_CREATION || objectHandle→Name || creationHash))
*/
void
TicketComputeCreation(
    TPMI_RH_HIERARCHY    hierarchy,     // IN: hierarchy for ticket
    TPM2B_NAME          *name,          // IN: object name
    TPM2B_DIGEST        *creation,      // IN: creation hash
    TPMT_TK_CREATION    *ticket         // OUT: created ticket
    )
{
    TPM2B_PROOF         *proof;
    HMAC_STATE           hmacState;

    // Get proper proof
    proof = HierarchyGetProof(hierarchy);

    // Fill in ticket fields
    ticket->tag = TPM_ST_CREATION;
    ticket->hierarchy = hierarchy;

    // Start HMAC using hierarchy proof as HMAC key
    ticket->digest.t.size = CryptHmacStart2B(&hmacState, CONTEXT_INTEGRITY_HASH_ALG,
                                             &proof->b);
        //  TPM_ST_CREATION
    CryptDigestUpdateInt(&hmacState, sizeof(TPM_ST), ticket->tag);
        //  name if provided
    if(name != NULL)
        CryptDigestUpdate2B(&hmacState.hashState, &name->b);
        //  creation hash
    CryptDigestUpdate2B(&hmacState.hashState, &creation->b);
        // Done
    CryptHmacEnd2B(&hmacState, &ticket->digest.b);

    return;
}